import asyncio
from functools import wraps
from xpander_sdk.exceptions import ModuleException
def retry_on_failure(max_retries=3, delay=1, backoff=2, retriable_codes=None):
if retriable_codes is None:
retriable_codes = [429, 500, 502, 503, 504]
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries + 1):
try:
return await func(*args, **kwargs)
except ModuleException as e:
last_exception = e
if e.status_code not in retriable_codes:
# Don't retry for non-retriable errors
raise
if attempt == max_retries:
# Last attempt, raise the exception
raise
wait_time = delay * (backoff ** attempt)
print(f"Attempt {attempt + 1} failed: {e.description}")
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
# This should never be reached, but just in case
raise last_exception
return wrapper
return decorator
# Usage
@retry_on_failure(max_retries=3, delay=2)
async def fetch_agent_with_retry(agent_id: str):
agents = Agents()
return await agents.aget(agent_id)
# Call the function
try:
agent = await fetch_agent_with_retry("agent-123")
print(f"Successfully loaded agent: {agent.name}")
except ModuleException as e:
print(f"Failed after all retries: {e.description}")